﻿#region Using Statements
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Content;
using System;
using System.Collections.Generic;
#endregion

namespace FierceWar
{
    class Ship : ElementalModel
    {
		private List<Enemy> _enemies;
		public List<Enemy> Enemies
		{
			get
			{
				return _enemies;
			}
			set
			{
				_enemies = value;
				_killedEnemiesCount = 0;
				_allEnemies = _enemies.Count;
			}
		}

		private int _killedEnemiesCount = 0;
		private int _allEnemies = 0;
        #region Fields
		private GameResources _resources;
        private GraphicsDevice _device;
		private BoundingBoxTree _bbt;
        private BoundingBoxTree _bbtEnemy;

		private ProgressBar _healthBar;
		private ProgressBar _manaBar;
		private Rectangle _healthBarRectangle;

		private List<Bullet> _bullets = new List<Bullet>();

		private int _bulletsCount;
		private float _stopwatchForBullet;
		private float _stopwatchForChargeBullet;
		private float _stopwatchForEnableText;
		private float _stopwatchForDisableText;
		private float _stopwatchForRecovery;

		private PlayerLevel _level;
		public int Level
		{
			get
			{
				return _level.LevelId;
			}
			set
			{
				_level = new PlayerLevel(value);

				_bulletsCount = _level.BulletsCapacity;
				_stopwatchForBullet = _level.TimeToNextBullet;
				_stopwatchForChargeBullet = _level.TimeToChargeBullet;
				_stopwatchForEnableText = _level.TimeToEnableText;
				_stopwatchForDisableText = _level.TimeToDisableText;
				_stopwatchForRecovery = _level.TimeToRecovery;
			}
		}

		public bool IsFPSCamera = false;

		#region Physic's properties.
		public Vector3 Direction;
		public Vector3 CurrentDirection;
		public Vector3 Up;

		private Vector3 _right;
		public Vector3 Right
		{
			get
			{
				return _right;
			}
		}

		private Vector3 _velocity;
		public Vector3 Velocity
		{
			get
			{
				return _velocity;
			}
			set
			{
				_velocity = value;
			}
		}

		private float _minimumAltitude = 2000.0f;
		private float _rotationRate = 1.5f;
		private float _thrustForce = 18000.0f;
		private float _dragFactor = 0.97f;
		private float _mass = 1.0f;
		#endregion
        #endregion

        public Ship(GameResources resources, GraphicsDevice device,
            ContentManager content, ModelType modelType, BoundingBoxTree bbt, BoundingBoxTree bbtEnemy, List<Enemy> enemies)
            : base(content, modelType)
        {
			_resources = resources;
            _device = device;
			_bbt = bbt;
            _bbtEnemy = bbtEnemy;
            Enemies = enemies;

			Level = 1;

			int width = 342;
			int height = 16;
			_healthBar = new ProgressBar(resources,
				new Vector2(
					(resources.GraphicsDevice.Viewport.Width - width) / 2 + 53.0f,
					(resources.GraphicsDevice.Viewport.Height - height) / 2 + 228.0f),
				0.0f, _level.Health, _level.Health, width, height, 2, 2,
				Color.Black * 0.2f, Color.Transparent, Color.Red * 0.5f);

			_manaBar = new ProgressBar(resources,
				new Vector2(
					(resources.GraphicsDevice.Viewport.Width - width) / 2 + 53.0f,
					(resources.GraphicsDevice.Viewport.Height - height) / 2 + 254.5f),
				0.0f, _level.Mana, _level.Mana, width, height, 2, 2,
				Color.Black * 0.2f, Color.Transparent, Color.SteelBlue);

			// Heath bar texture.
			_healthBarRectangle = new Rectangle(
				(resources.GraphicsDevice.Viewport.Width - resources.HeathBar.Width) / 2,
				(resources.GraphicsDevice.Viewport.Height - resources.HeathBar.Height) / 2 + 242,
				resources.HeathBar.Width, resources.HeathBar.Height);

            Reset();
        }

        #region Methods

        /// <summary>
        /// Restore the ship to its original state.
        /// </summary>
        public void Reset()
        {
            Position = new Vector3(0.0f, _minimumAltitude, 0.0f);
            Direction = Vector3.Forward;
			Rotation = Vector3.Zero;
            Up = Vector3.Up;
            _right = Vector3.Right;
            _velocity = Vector3.Zero;
        }

        /// <summary>
        /// Applies a simple rotation to the ship.
        /// </summary>
        /// <param name="gameTime">The game time.</param>
        public bool Update(GameTime gameTime)
        {
            float elapsedTime = (float)gameTime.ElapsedGameTime.TotalSeconds;

			#region Movement.
			// Determine rotation amount from keyboard input.
			if (!IsFPSCamera &&
				(KeyboardInput.IsKeyDown(Keys.Up) ||
				 KeyboardInput.IsKeyDown(Keys.I) ||
				 MouseInput.ClickTop))
			{
				Rotation.X += _rotationRate * elapsedTime;
			}
			if (!IsFPSCamera &&
				(KeyboardInput.IsKeyDown(Keys.Down) ||
				 KeyboardInput.IsKeyDown(Keys.K) ||
				 MouseInput.ClickBottom))
			{
				Rotation.X -= _rotationRate * elapsedTime;
			}
			if (!IsFPSCamera &&
				(KeyboardInput.IsKeyDown(Keys.Left) ||
				 KeyboardInput.IsKeyDown(Keys.A) ||
				 MouseInput.ClickLeft))
			{
				if (Up.Y < 0)
				{
					Rotation.Y -= _rotationRate * elapsedTime;
				}
				else
				{
					Rotation.Y += _rotationRate * elapsedTime;
				}
			}
			if (!IsFPSCamera &&
				(KeyboardInput.IsKeyDown(Keys.Right) ||
				 KeyboardInput.IsKeyDown(Keys.D) ||
				 MouseInput.ClickRight))
			{
				if (Up.Y < 0)
				{
					Rotation.Y += _rotationRate * elapsedTime;
				}
				else
				{
					Rotation.Y -= _rotationRate * elapsedTime;
				}
			}

			// Rotation orientation vectors.
			Direction = Vector3.TransformNormal(Vector3.Forward, World);
			Up = Vector3.TransformNormal(Vector3.Up, World);

			// Normalize orientation vectors.
			Direction.Normalize();
			Up.Normalize();

			// Calculcate right vector.
			_right = Vector3.Cross(Direction, Up);

			// Re-calculate direction vector in order to ensure 
			// three orientation vectors are orthogonal each other.
			Direction = Vector3.Cross(Up, _right);

			// Determine thrust amount from keyboard input.
			float thrustAmount = 0.0f;
			if (KeyboardInput.IsKeyDown(Keys.W) ||
				(!IsFPSCamera && MouseInput.IsLeftPress))
			{
				thrustAmount = 1.0f;
				CurrentDirection = Direction;
			}
			else if (KeyboardInput.IsKeyDown(Keys.S))
			{
				thrustAmount = 1.0f;
				CurrentDirection = -Direction;
			}
			else
			{
				CurrentDirection = Direction;
			}

			if (KeyboardInput.IsKeyPressed(Keys.Space))
			{
				if (_manaBar.Value >= 50.0f)
				{
					_manaBar.Value -= 50.0f;
					thrustAmount = 120.0f;
				}
			}

			float thrustCrossAmount = 0.0f;
			if (IsFPSCamera)
			{
				if (KeyboardInput.IsKeyDown(Keys.A) ||
					KeyboardInput.IsKeyDown(Keys.Up))
				{
					thrustCrossAmount = -100.0f;
				}

				if (KeyboardInput.IsKeyDown(Keys.D) ||
					KeyboardInput.IsKeyDown(Keys.Down))
				{
					thrustCrossAmount = 100.0f;
				}
			}

			// Calculate force from thrust amount.
			Vector3 force = CurrentDirection * thrustAmount * _thrustForce;

			// Apply accelaration.
			Vector3 accelaration = force / _mass;

			_velocity += accelaration * elapsedTime;

			// Apply psuedo drag.
			_velocity *= _dragFactor;

			// Apply velocity.
			Position += _velocity * elapsedTime + thrustCrossAmount * _right;

			// Prevent the ship from flying under the ground.
			Position = new Vector3(
				MathHelper.Clamp(Position.X, -65536.0f, 65536.0f),
				MathHelper.Clamp(Position.Y, _minimumAltitude, 65536.0f),
				MathHelper.Clamp(Position.Z, -65536.0f, 65536.0f)); 
			#endregion

			// Update health bar.
			_stopwatchForRecovery -=
				(float)gameTime.ElapsedGameTime.TotalMilliseconds;
			if (_stopwatchForRecovery < 0.0f)
			{
				_stopwatchForRecovery = _level.TimeToRecovery;
				_healthBar.Value += _level.HealthRecovery;
				_manaBar.Value += _level.ManaRecovery;
			}

			_healthBar.Update();
			_manaBar.Update();

			foreach (Bullet bullet in _bullets)
			{
				bullet.Update();
			}

			for (int i = 0; i < _bullets.Count; i++)
			{
                Enemy enemy = (Enemy)_bbtEnemy.IsShooted(_bullets[i]);
                if (enemy != null)
                {
					if (enemy.IsShooted() == false)
					{
						Enemies.Remove(enemy);
						_bbtEnemy.Remove(enemy);
						_killedEnemiesCount++;
						_resources.Explode.Play();
					}

                    _bullets.Remove(_bullets[i]);
                }
                else if (_bullets[i].IsOutsideRange ||
                    _bbt.HasCollision(_bullets[i]))
                {
                    _bullets.Remove(_bullets[i]);
                }
			}

			#region Bullets.
			// Shoot.
			if (_bulletsCount > 0)
			{
				if (KeyboardInput.IsKeyPressed(Keys.J) ||
					(IsFPSCamera && MouseInput.IsLeftClick))
				{
					_bullets.Add(new Bullet(_resources.Content, ModelType.Bullet,
						Position + Direction * 700, Rotation, Direction));
					_bulletsCount--;
					_resources.Hit.Play();
				}
				else if (KeyboardInput.IsKeyDown(Keys.J) ||
					(IsFPSCamera && MouseInput.IsLeftPress))
				{
					_stopwatchForBullet -=
						(float)gameTime.ElapsedGameTime.TotalMilliseconds;
					if (_stopwatchForBullet < 0.0f)
					{
						_stopwatchForBullet = _level.TimeToNextBullet;
						_bullets.Add(new Bullet(_resources.Content, ModelType.Bullet,
							Position, Rotation, Direction));
						_bulletsCount--;
						_resources.Hit.Play();
					}
				}
				else
				{
					_stopwatchForBullet = _level.TimeToNextBullet;
				}
			}
			else
			{
				_stopwatchForChargeBullet -=
					(float)gameTime.ElapsedGameTime.TotalMilliseconds;
				if (_stopwatchForChargeBullet < 0.0f)
				{
					_stopwatchForChargeBullet = _level.TimeToChargeBullet;
					_bulletsCount = _level.BulletsCapacity;
				}
			}

			// Charge a gun.
			if (KeyboardInput.IsKeyPressed(Keys.LeftShift))
			{
				if (_bulletsCount < _level.BulletsCapacity)
				{
					_bulletsCount = 0;
				}
			}
			#endregion

            if (Enemies.Count == 0)
                return false;
            else
                return true;
        }

		public void Draw(Matrix View, Matrix Projection, 
			Vector3 CameraPosition, GameTime gameTime)
		{
			base.Draw(View, Projection, CameraPosition);

			_resources.SpriteBatch.Draw(_resources.HeathBar,
				_healthBarRectangle, Color.White);

			_healthBar.Draw();
			_manaBar.Draw();

			foreach (Bullet bullet in _bullets)
			{
				bullet.Draw(View, Projection, Position);
			}

			string levelText = "Level: " + Level +
				"\nKilled: " + _killedEnemiesCount + "/" + _allEnemies;
			Vector2 fontLevelText =
				_resources.FigureFont.MeasureString(levelText.ToString());
			_resources.SpriteBatch.DrawString(_resources.FigureFont,
					levelText.ToString(),
					new Vector2(
						(_resources.GraphicsDevice.Viewport.Width - fontLevelText.X) / 2 - 470,
						(_resources.GraphicsDevice.Viewport.Height - fontLevelText.Y) / 2 + 240),
					Color.White);

			if (_bulletsCount > 0)
			{
				string text = "Bullets: " + _bulletsCount;
				Vector2 fontText =
						_resources.FigureFont.MeasureString(text.ToString());
				_resources.SpriteBatch.DrawString(_resources.FigureFont,
					text.ToString(),
					new Vector2(
						(_resources.GraphicsDevice.Viewport.Width - fontText.X) / 2 + 470,
						(_resources.GraphicsDevice.Viewport.Height - fontText.Y) / 2 + 260),
					Color.White);

				if (_resources.ChargeInstance.State == Microsoft.Xna.Framework.Audio.SoundState.Playing)
				{
					_resources.ChargeInstance.Stop();
				}
			}
			else
			{
				_stopwatchForEnableText -=
					(float)gameTime.ElapsedGameTime.TotalMilliseconds;
				if (_stopwatchForEnableText < 0.0f)
				{
					_stopwatchForDisableText -=
						(float)gameTime.ElapsedGameTime.TotalMilliseconds;
					if (_stopwatchForDisableText < 0.0f)
					{
						_stopwatchForDisableText = _level.TimeToDisableText;
						_stopwatchForEnableText = _level.TimeToEnableText;
					}
				}
				else
				{
					Vector2 fontText =
									_resources.FigureFont.MeasureString("Charging...");
					_resources.SpriteBatch.DrawString(_resources.FigureFont,
						"Charging...",
						new Vector2(
							(_resources.GraphicsDevice.Viewport.Width - fontText.X) / 2 + 470,
							(_resources.GraphicsDevice.Viewport.Height - fontText.Y) / 2 + 260),
						Color.White);
					if (_resources.ChargeInstance.State == Microsoft.Xna.Framework.Audio.SoundState.Stopped)
					{
						_resources.ChargeInstance.Play();
					}
				}
			}
		}
        #endregion
    }
}
